//
// Created by twetec on 25-7-13.
//

#include "handle_udp_probe_task.h"

#include <sys/socket.h>
#include <arpa/inet.h>

#include <bs_thread_pool/BS_thread_pool.hpp>

#include "../../rpc/src/perception_rpc_caller_register_probe.h"

using namespace std::chrono_literals;

namespace network_prober::probe
{
// 线程池
static BS::thread_pool s_iopool{1500};

// UDPProbeIPv4::UDPProbeIPv4(const std::string &interface, const std::string &target, const std::uint32_t& port)
//     : m_interface(interface), m_target(target), m_port(port)
// {
//     // 连接的超时时间
//     m_UDPClient.set_connect_timeout(std::chrono::seconds(13));
//     // 断开重连
//     m_UDPClient.set_auto_reconnect(false);
//
//     // 在socket初始化时调用
//     m_UDPClient.bind_init([this]() {
//         // 绑定请求从哪个网口发送
//          asio::ip::udp::endpoint endpoint(asio::ip::address_v4::from_string(m_interface), 0);
//          m_UDPClient.socket().bind(endpoint);
//     });
// }
//
// void UDPProbeIPv4::setOnProbedCallback(std::function<void(std::int64_t)> callback)
// {
//     m_onProbedCallback = std::move(callback);
//
//     // 指向当前对象的shared_ptr
//     auto self{shared_from_this()};
//     // 连接成功时调用
//     m_UDPClient.bind_connect([this, self]() {
//         // 回调, 返回探测结果
//         m_onProbedCallback(asio2::get_last_error()? -1 : std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - m_probeStartTime).count());
//         // 断开连接，回收资源
//         m_UDPClient.stop();
//
//         // 该代码块执行完， self被释放，当前对象计数器减1.释放当前对象
//     });
// }
//
// void UDPProbeIPv4::probe()
// {
//     // 建立TCP连接
//     m_UDPClient.async_start(m_target, m_port);
//     // 建立连接时，开始计时
//     m_probeStartTime = std::chrono::steady_clock::now();
// }
//
// UDPProbeIPv4::~UDPProbeIPv4()
// {
//     m_UDPClient.destroy();
// }

HandleUDPProbeIPv4Awaiter::HandleUDPProbeIPv4Awaiter(const database::ProbeTaskModel &probeTask)
    : m_interface(probeTask.interface), m_target(probeTask.address), m_port(probeTask.port)
{
}

bool HandleUDPProbeIPv4Awaiter::await_ready()
{
    return false;
}

void HandleUDPProbeIPv4Awaiter::await_suspend(std::coroutine_handle<> handle)
{
    s_iopool.submit_task([this, handle] {
        // 预设延迟未-1
        m_delay = -1;

        // 这里定循环，就是在套接字中途失效时，重新尝试申请
        for (std::size_t i = 0; i < 3; i ++) {
            // 创建UDP套接字
            int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
            // 创建失败时结束探测
            if (sockfd < 0) {
                // 输出日志
                SPDLOG_INFO("sockfd创建失败: {}", strerror(errno));
                // 继续尝试获取套接字
                continue;
            }

            // 设置接收超时(8s)
            struct timeval tv;
            tv.tv_sec = 6000 / 1000;
            tv.tv_usec = (6000 % 1000) * 1000;
            if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
                // 关闭socket
                close(sockfd);
                // 输出日志
                SPDLOG_INFO("sockfd: {} - 超时设置时间失败: {}", sockfd, strerror(errno));
                // 判断错误是否是套接字
                if (errno == EBADF) continue;
                // 结束执行
                break;
            }

            // 设定网口地址
            struct sockaddr_in interfaceAddr;
            memset(&interfaceAddr, 0, sizeof(interfaceAddr));
            interfaceAddr.sin_family = AF_INET;
            interfaceAddr.sin_port = htons(0);
            if (inet_pton(AF_INET, m_interface.c_str(), &interfaceAddr.sin_addr) <= 0) {
                // 关闭socket
                close(sockfd);
                // 输出日志
                SPDLOG_INFO("sockfd: {} - 网口地址设置失败: {}", sockfd, strerror(errno));
                // 判断错误是否是套接字
                if (errno == EBADF) continue;
                // 结束执行
                break;;
            }

            // 绑定本机网口
            if (bind(sockfd, (sockaddr *)&interfaceAddr, sizeof(interfaceAddr)) < 0) {
                // 关闭socket
                close(sockfd);
                // 输出日志
                SPDLOG_INFO("sockfd: {} - 网口地址绑定失败: {}", sockfd, strerror(errno));
                // 判断错误是否是套接字
                if (errno == EBADF) continue;
                // 结束执行
                break;
            }

            // 设置目标地址
            struct sockaddr_in targetAddr;
            memset(&targetAddr, 0, sizeof(targetAddr));
            targetAddr.sin_family = AF_INET;
            targetAddr.sin_port = htons(m_port);
            if (inet_pton(AF_INET, m_target.c_str(), &targetAddr.sin_addr) <= 0) {
                // 关闭socket
                close(sockfd);
                // 输出日志
                SPDLOG_INFO("sockfd: {} - 目标地址设置失败: {}", sockfd, strerror(errno));
                // 判断错误是否是套接字
                if (errno == EBADF) continue;
                // 结束执行
                break;
            }

            // 请求计时 - 开始时间
            std::chrono::time_point<std::chrono::steady_clock> probeStartTime = std::chrono::steady_clock::now();

            // 发送探测数据包
            const char* message = "PING";
            if (sendto(sockfd, message, strlen(message), 0, (const struct sockaddr*)&targetAddr, sizeof(targetAddr)) < 0) {
                // 关闭socket
                close(sockfd);
                // 输出日志
                SPDLOG_INFO("sockfd: {} - 探测数据包发送失败: {}", sockfd, strerror(errno));
                // 判断错误是否是套接字
                if (errno == EBADF) continue;
                // 结束执行
                break;
            }

            // 尝试接收响应
            char buffer[1024];
            socklen_t len = sizeof(targetAddr);
            ssize_t recvdBytesCount = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&targetAddr, &len);

            // 关闭socket
            close(sockfd);

            // 如果未接收到数据
            if (recvdBytesCount <= 0) {
                // 输出日志
                // SPDLOG_INFO("sockfd: {} - 数据包接收失败: {}", sockfd, strerror(errno));
                // 判断错误是否是套接字
                if (errno == EBADF) continue;
                // 结束执行
                break;
            }

            // 接收到数据，计算延迟
            m_delay = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - probeStartTime).count();
            // 输出日志
            SPDLOG_INFO("观测任务执行成功: {} 延迟", m_delay);
            // 结束执行
            break;
        }

        // 在m_delay计算完后，调度
        handle.resume();
    });
}

std::int64_t HandleUDPProbeIPv4Awaiter::await_resume()
{
    return m_delay;
}


// UDPProbeIPv6::UDPProbeIPv6(const std::string &interface, const std::string &target, const std::uint32_t& port)
//     : m_interface(interface), m_target(target), m_port(port)
// {
//     // 连接的超时时间
//     m_UDPClient.set_connect_timeout(std::chrono::seconds(13));
//     // 断开重连
//     m_UDPClient.set_auto_reconnect(false);
//
//     // 在socket初始化时调用
//     m_UDPClient.bind_init([this]() {
//         // 绑定请求从哪个网口发送
//         asio::ip::udp::endpoint endpoint(asio::ip::address_v6::from_string(m_interface), 0);
//         m_UDPClient.socket().bind(endpoint);
//     });
// }
//
// void UDPProbeIPv6::setOnProbedCallback(std::function<void(std::int64_t)> callback)
// {
//     m_onProbedCallback = std::move(callback);
//
//     // 指向当前对象的shared_ptr
//     auto self{shared_from_this()};
//     // 连接成功时调用
//     m_UDPClient.bind_connect([this, self]() {
//         // 回调, 返回探测结果
//         m_onProbedCallback(asio2::get_last_error()? -1 : std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - m_probeStartTime).count());
//         // 断开连接，回收资源
//         m_UDPClient.stop();
//
//         // 该代码块执行完， self被释放，当前对象计数器减1.释放当前对象
//     });
// }
//
// void UDPProbeIPv6::probe()
// {
//     // 建立TCP连接
//     m_UDPClient.async_start(m_target, m_port);
//     // 建立连接时，开始计时
//     m_probeStartTime = std::chrono::steady_clock::now();
// }
//
// UDPProbeIPv6::~UDPProbeIPv6()
// {
//     m_UDPClient.destroy();
// }

HandleUDPProbeIPv6Awaiter::HandleUDPProbeIPv6Awaiter(const database::ProbeTaskModel &probeTask)
    : m_interface(probeTask.interface), m_target(probeTask.address), m_port(probeTask.port)
{
}

bool HandleUDPProbeIPv6Awaiter::await_ready()
{
    return false;
}

void HandleUDPProbeIPv6Awaiter::await_suspend(std::coroutine_handle<> handle)
{
    s_iopool.submit_task([this, handle] {
        // 预设延迟未-1
        m_delay = -1;

        // 这里定循环，就是在套接字中途失效时，重新尝试申请
        for (std::size_t i = 0; i < 3; i ++) {
            // 创建UDP套接字
            int sockfd = socket(AF_INET6, SOCK_DGRAM, 0);
            // 创建失败时结束探测
            if (sockfd < 0) {
                // 输出日志
                SPDLOG_INFO("sockfd创建失败: {}", strerror(errno));
                // 继续尝试获取套接字
                continue;
            }

            // 设置接收超时(8s)
            struct timeval tv;
            tv.tv_sec = 6000 / 1000;
            tv.tv_usec = (6000 % 1000) * 1000;
            if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
                // 关闭socket
                close(sockfd);
                // 输出日志
                SPDLOG_INFO("sockfd: {} - 超时设置时间失败: {}", sockfd, strerror(errno));
                // 判断错误是否是套接字
                if (errno == EBADF) continue;
                // 结束执行
                break;
            }

            // // 设定网口地址
            // struct sockaddr_in6 interfaceAddr;
            // memset(&interfaceAddr, 0, sizeof(interfaceAddr));
            // interfaceAddr.sin6_family = AF_INET6;
            // interfaceAddr.sin6_port = htons(0);
            // if (inet_pton(AF_INET6, m_interface.c_str(), &interfaceAddr.sin6_addr) <= 0) {
            //     // 关闭socket
            //     close(sockfd);
            //     // 输出日志
            //     SPDLOG_INFO("sockfd: {} - 网口地址设置失败: {}", sockfd, strerror(errno));
            //     // 判断错误是否是套接字
            //     if (errno == EBADF) continue;
            //     // 结束执行
            //     break;;
            // }
            //
            // // 绑定本机网口
            // if (bind(sockfd, (sockaddr *)&interfaceAddr, sizeof(interfaceAddr)) < 0) {
            //     // 关闭socket
            //     close(sockfd);
            //     // 输出日志
            //     SPDLOG_INFO("sockfd: {} - 网口地址绑定失败: {}", sockfd, strerror(errno));
            //     // 判断错误是否是套接字
            //     if (errno == EBADF) continue;
            //     // 结束执行
            //     break;
            // }

            // 设置目标地址
            struct sockaddr_in6 targetAddr;
            memset(&targetAddr, 0, sizeof(targetAddr));
            targetAddr.sin6_family = AF_INET6;
            targetAddr.sin6_port = htons(m_port);
            if (inet_pton(AF_INET6, m_target.c_str(), &targetAddr.sin6_addr) <= 0) {
                // 关闭socket
                close(sockfd);
                // 输出日志
                SPDLOG_INFO("sockfd: {} - 目标地址设置失败: {}", sockfd, strerror(errno));
                // 判断错误是否是套接字
                if (errno == EBADF) continue;
                // 结束执行
                break;
            }

            // 请求计时 - 开始时间
            std::chrono::time_point<std::chrono::steady_clock> probeStartTime = std::chrono::steady_clock::now();

            // 发送探测数据包
            const char* message = "PING";
            if (sendto(sockfd, message, strlen(message), 0, (const struct sockaddr*)&targetAddr, sizeof(targetAddr)) < 0) {
                // 关闭socket
                close(sockfd);
                // 输出日志
                SPDLOG_INFO("sockfd: {} - 探测数据包发送失败: {}", sockfd, strerror(errno));
                // 判断错误是否是套接字
                if (errno == EBADF) continue;
                // 结束执行
                break;
            }

            // 尝试接收响应
            char buffer[1024];
            socklen_t len = sizeof(targetAddr);
            ssize_t recvdBytesCount = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&targetAddr, &len);

            // 关闭socket
            close(sockfd);

            // 如果未接收到数据
            if (recvdBytesCount <= 0) {
                // 输出日志
                // SPDLOG_INFO("sockfd: {} - 数据包接收失败: {}", sockfd, strerror(errno));
                // 判断错误是否是套接字
                if (errno == EBADF) continue;
                // 结束执行
                break;
            }

            // 接收到数据，计算延迟
            m_delay = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - probeStartTime).count();
            // 输出日志
            SPDLOG_INFO("观测任务执行成功: {} 延迟", m_delay);
            // 结束执行
            break;
        }

        // 在m_delay计算完后，调度
        handle.resume();
    });
}

std::int64_t HandleUDPProbeIPv6Awaiter::await_resume()
{
    return m_delay;
}


}